"""
认证服务
"""
from sqlalchemy.orm import Session
from datetime import datetime
from typing import Optional, Tuple
from fastapi import HTTPException, status

from models.models import User
from schemas.auth import UserRegister, UserLogin
from tools.auth import get_password_hash, verify_password, create_access_token, create_refresh_token
from utils.sms import sms_client
from patterns.observer import EventManager, Event, EventType
# 临时禁用钉钉登录功能
try:
    from tools.dingtalk import dingtalk_client
except ImportError:
    dingtalk_client = None
import redis

# Redis客户端（用于存储验证码）
try:
    redis_client = redis.Redis(
        host='localhost',
        port=6379,
        db=0,
        decode_responses=True
    )
    redis_client.ping()  # 测试连接
except:
    redis_client = None


class AuthService:
    """认证服务类"""
    
    @staticmethod
    def register_user(db: Session, user_data: UserRegister) -> User:
        """用户注册 (集成观察者模式)"""
        # 检查用户名是否已存在
        existing_user = db.query(User).filter(
            (User.username == user_data.username) |
            (User.phone == user_data.phone) |
            (User.email == user_data.email)
        ).first()
        
        if existing_user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="用户名、手机号或邮箱已被注册"
            )
        
        # 创建新用户
        user = User(
            username=user_data.username,
            password_hash=get_password_hash(user_data.password),
            nickname=user_data.nickname,
            phone=user_data.phone,
            email=user_data.email,
            status=1,
            created_at=datetime.now(),
            updated_at=datetime.now()
        )
        
        db.add(user)
        db.commit()
        db.refresh(user)
        
        # === 发布用户注册事件 ===
        event_manager = EventManager()
        event = Event(
            event_type=EventType.USER_REGISTERED,
            data={
                'user': user,
                'db': db,
                'registration_type': 'password'
            },
            user_id=user.id
        )
        event_manager.publish(event)
        
        return user
    
    @staticmethod
    def login_by_password(db: Session, login_data: UserLogin) -> Tuple[str, User]:
        """账密登录 (集成观察者模式)"""
        # 查找用户（支持用户名或手机号登录）
        user = db.query(User).filter(
            (User.username == login_data.username) |
            (User.phone == login_data.username)
        ).first()
        
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误"
            )
        
        # 验证密码
        if not verify_password(login_data.password, user.password_hash):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误"
            )
        
        # 检查用户状态
        if user.status == 0:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="账号已被禁用"
            )
        
        # 生成JWT token和Refresh Token
        access_token = create_access_token(data={"sub": str(user.id)})
        refresh_token = create_refresh_token(data={"sub": str(user.id)})
        
        # === 发布用户登录事件 ===
        event_manager = EventManager()
        event = Event(
            event_type=EventType.USER_LOGIN,
            data={
                'user': user,
                'login_method': 'password'
            },
            user_id=user.id
        )
        event_manager.publish(event)
        
        return access_token, refresh_token, user  # 返回Refresh Token用于续命
    
    @staticmethod
    def send_verification_code(phone: str) -> Tuple[bool, str]:
        """发送短信验证码"""
        if not redis_client:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Redis服务未配置，无法发送验证码"
            )
        
        # 检查是否在60秒内发送过
        cache_key = f"sms:{phone}"
        if redis_client.exists(cache_key):
            raise HTTPException(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                detail="请勿频繁发送验证码，请稍后再试"
            )
        
        # 生成6位验证码
        code = sms_client.generate_code(6)
        
        # 发送短信（实际调用容联云API）
        success, message = sms_client.send_verification_code(phone, code)
        
        if success:
            # 存储验证码，有效期5分钟
            redis_client.setex(f"sms:{phone}", 300, code)
            return True, "验证码发送成功"
        else:
            return False, message
    
    @staticmethod
    def login_by_code(db: Session, phone: str, code: str) -> Tuple[str, User]:
        """验证码登录 (集成观察者模式)"""
        if not redis_client:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Redis服务未配置"
            )
        
        # 验证验证码
        cache_key = f"sms:{phone}"
        stored_code = redis_client.get(cache_key)
        
        if not stored_code or stored_code != code:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="验证码错误或已过期"
            )
        
        # 查找或创建用户
        user = db.query(User).filter(User.phone == phone).first()
        is_new_user = False
        
        if not user:
            # 首次登录，自动注册
            is_new_user = True
            user = User(
                username=phone,
                password_hash=get_password_hash(""),  # 验证码登录不需要密码
                nickname=f"用户{phone[-4:]}",
                phone=phone,
                status=1,
                created_at=datetime.now(),
                updated_at=datetime.now()
            )
            db.add(user)
            db.commit()
            db.refresh(user)
            
            # === 发布用户注册事件 ===
            event_manager = EventManager()
            event = Event(
                event_type=EventType.USER_REGISTERED,
                data={
                    'user': user,
                    'db': db,
                    'registration_type': 'code'
                },
                user_id=user.id
            )
            event_manager.publish(event)
        else:
            # 检查用户状态
            if user.status == 0:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="账号已被禁用"
                )
        
        # 删除验证码
        redis_client.delete(cache_key)
        
        # 生成JWT token和Refresh Token
        access_token = create_access_token(data={"sub": str(user.id)})
        refresh_token = create_refresh_token(data={"sub": str(user.id)})
        
        # === 发布用户登录事件 ===
        if not is_new_user:
            event_manager = EventManager()
            event = Event(
                event_type=EventType.USER_LOGIN,
                data={
                    'user': user,
                    'login_method': 'code'
                },
                user_id=user.id
            )
            event_manager.publish(event)
        
        return access_token, refresh_token, user  # 返回Refresh Token用于续命
    
    @staticmethod
    def get_user_by_id(db: Session, user_id: int) -> Optional[User]:
        """根据ID获取用户"""
        return db.query(User).filter(User.id == user_id).first()
    
    @staticmethod
    def refresh_access_token(db: Session, refresh_token: str) -> Tuple[str, str, User]:
        """刷新Access Token（Token续命）"""
        from tools.auth import verify_refresh_token, create_access_token, create_refresh_token
        
        payload = verify_refresh_token(refresh_token)
        if not payload:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的Refresh Token")
        
        user_id = int(payload.get("sub"))
        user = AuthService.get_user_by_id(db, user_id)
        if not user or user.status == 0:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在或已禁用")
        
        # 生成新的Token对
        new_access_token = create_access_token(data={"sub": str(user.id)})
        new_refresh_token = create_refresh_token(data={"sub": str(user.id)})
        return new_access_token, new_refresh_token, user  # 返回新的Token对用于续命
    
    @staticmethod
    async def login_by_dingtalk(db: Session, code: str) -> Tuple[str, str, User]:
        """钉钉登录"""
        print(f"\n{'='*50}")
        print(f"钉钉登录开始")
        print(f"{'='*50}")
        print(f"收到的 code: {code}")
        print(f"code 类型: {type(code)}")
        print(f"code 长度: {len(code) if code else 0}")
        
        try:
            if not dingtalk_client:
                print("[ERROR] 钉钉客户端未初始化")
                raise HTTPException(
                    status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
                    detail="钉钉登录功能不可用，请检查SDK安装"
                )
            
            print("[OK] 钉钉客户端已初始化")
            print("AppKey:", dingtalk_client.client_id)
            print("尝试获取用户信息...")
            
            # 获取钉钉用户信息
            dingtalk_user_info = await dingtalk_client.get_user_info_by_code(code)
            
            print(f"获取用户信息结果: {dingtalk_user_info is not None}")
            
            if not dingtalk_user_info:
                print("[ERROR] 无法获取用户信息")
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="钉钉登录失败，无法获取用户信息。请检查：1) 钉钉App配置是否正确 2) 回调地址是否配置 3) code是否有效"
                )
        except HTTPException:
            raise
        except Exception as e:
            print(f"[ERROR] 钉钉登录处理失败: {type(e).__name__}")
            print(f"错误信息: {str(e)}")
            import traceback
            traceback.print_exc()
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"钉钉登录处理失败: {str(e)}"
            )
        
        union_id = dingtalk_user_info.get("unionId")
        open_id = dingtalk_user_info.get("openId")
        
        print(f"union_id: {union_id}")
        print(f"open_id: {open_id}")
        
        if not union_id and not open_id:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="无法获取钉钉用户ID"
            )
        
        # 使用union_id或open_id作为唯一标识
        if not union_id:
            union_id = open_id
        
        # 构建唯一的用户名和手机号
        # 确保不超过数据库字段限制
        username = f"ding_{union_id}"[:50]  # 限制在50字符内
        phone = f"ding_{union_id}"[:50]     # 限制在50字符内
        
        print(f"生成的username: {username}")
        print(f"生成的phone: {phone}")
        
        # 查找或创建用户（使用username作为主要标识）
        user = db.query(User).filter(User.username == username).first()
        is_new_user = False
        
        if not user:
            # 首次登录，自动注册
            is_new_user = True
            nickname = dingtalk_user_info.get("nickname", f"钉钉用户{union_id[-8:]}")
            avatar_url = dingtalk_user_info.get("avatar_url")
            
            user = User(
                username=username,
                password_hash=get_password_hash(""),  # 第三方登录不需要密码
                nickname=nickname,
                phone=phone,
                avatar_url=avatar_url,
                status=1,
                created_at=datetime.now(),
                updated_at=datetime.now()
            )
            db.add(user)
            db.commit()
            db.refresh(user)
            
            # === 发布用户注册事件 ===
            event_manager = EventManager()
            event = Event(
                event_type=EventType.USER_REGISTERED,
                data={
                    'user': user,
                    'db': db,
                    'registration_type': 'dingtalk'
                },
                user_id=user.id
            )
            event_manager.publish(event)
        else:
            # 更新用户信息
            if "nickname" in dingtalk_user_info:
                user.nickname = dingtalk_user_info["nickname"]
            if "avatar_url" in dingtalk_user_info and dingtalk_user_info["avatar_url"]:
                user.avatar_url = dingtalk_user_info["avatar_url"]
            user.updated_at = datetime.now()
            db.commit()
            db.refresh(user)
            
            # 检查用户状态
            if user.status == 0:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="账号已被禁用"
                )
        
        # 生成JWT token和Refresh Token
        access_token = create_access_token(data={"sub": str(user.id)})
        refresh_token = create_refresh_token(data={"sub": str(user.id)})
        
        # === 发布用户登录事件 ===
        if not is_new_user:
            event_manager = EventManager()
            event = Event(
                event_type=EventType.USER_LOGIN,
                data={
                    'user': user,
                    'login_method': 'dingtalk'
                },
                user_id=user.id
            )
            event_manager.publish(event)
        
        return access_token, refresh_token, user  # 返回Refresh Token用于续命

